package com.kangda.parttitionstorage.part;

import android.content.Context;
import android.graphics.Bitmap;
import android.media.UnsupportedSchemeException;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import android.webkit.MimeTypeMap;
import android.widget.Toast;

import androidx.annotation.RequiresApi;

import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.schedulers.Schedulers;

/**
 * @Author Kang da
 * @Date 2021/11/30 5:38 下午
 * Android 11 适配的保存图片，文件方案，现支持，bitmap，base64，audio（mp3）,video（mp4）,text,excel,word,pdf,zip
 * 安卓11以下需要在对应文件创建文件，用来实现和Android 11 同样效果
 */

public class PartitionStorageUtil {

    /**
     * 存储bitmap图，仅支持png jpg jpeg
     *
     * @param bitmap   bitmap图
     * @param fileType 文件后缀名 无"."
     */
    public static void saveFile(Context appContext, Bitmap bitmap, String fileType, IFileSaveListener fileSaveListener) {
        Observable.just(bitmap)
                .map(new Function<Bitmap, Integer>() {
                    @Override
                    public Integer apply(Bitmap bit) throws Throwable {
                        if (!TextUtils.equals("png", fileType) && !TextUtils.equals("jpg", fileType) && !TextUtils.equals("jpeg", fileType)) {
                            throw new UnsupportedSchemeException("不支持的类型");
                        }
                        String mimeType = getMimeTypeByFileType(fileType);
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                            String fileName = String.valueOf(System.currentTimeMillis());
                            //所有图片默认以jpg方式存储
                            ContentHelper contentHelper = new ContentHelper(appContext, fileName, mimeType);
                            if (contentHelper.insertUri == null) {
                                Log.e("PartitionStorageUtil", "sandBoxToPublic insertUri is Null");
                                throw new Exception("sandBoxToPublic insertUri is Null");
                            }
                            OutputStream outputStream = contentHelper.resolver.openOutputStream(contentHelper.insertUri);
                            if (TextUtils.equals("png", fileType)) {
                                bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
                            } else {
                                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
                            }
                            outputStream.close();
                            contentHelper.values.clear();
                            contentHelper.values.put(MediaStore.MediaColumns.IS_PENDING, 0);
                            contentHelper.update();
                        } else {
                            File fileDir = PartStaticFinalUtil.getFileDirByType(mimeType);
                            File saveFile = new File(fileDir, System.currentTimeMillis() + "." + fileType);
                            OutputStream outputStream = null;
                            boolean isSuccess = false;
                            try {
                                outputStream = new FileOutputStream(saveFile);
                                if (TextUtils.equals("png", fileType)) {
                                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
                                } else {
                                    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
                                }
                                isSuccess = true;
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.flush();
                                        outputStream.close();
                                    }
                                    if (isSuccess){
                                        FileUtils.freshFile(appContext, saveFile);
                                    }
                                } catch (IOException e) {
                                }
                            }
                        }
                        return 1;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer flag) throws Throwable {
                        if (fileSaveListener != null) {
                            fileSaveListener.onComplete();
                        }
                        Toast.makeText(appContext, "保存成功！", Toast.LENGTH_SHORT).show();
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Throwable {
                        if (fileSaveListener != null) {
                            fileSaveListener.onError(throwable);
                        }
                        String eMessage = throwable.getMessage();
                        if (TextUtils.isEmpty(eMessage)){
                            eMessage = "保存失败";
                        }
                        Toast.makeText(appContext, eMessage, Toast.LENGTH_SHORT).show();
                    }
                });
    }

    /**
     * @param file 文件file
     */
    public static void saveFile(Context appContext, File file, IFileSaveListener fileSaveListener) {
        Observable.just(file)
                .map(new Function<File, Integer>() {
                    @Override
                    public Integer apply(File file) throws Throwable {
                        String fileName =
                                getFileNameNoExt(file.getName());
                        String fileType = MimeTypeMap.getFileExtensionFromUrl(file.getPath());
                        String mimeType = getMimeTypeByFileType(fileType);
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                            ContentHelper contentHelper = new ContentHelper(appContext, fileName, mimeType);
                            if (contentHelper.insertUri == null) {
                                Log.e("PartitionStorageUtil", "sandBoxToPublic insertUri is Null");
                                throw new Exception("sandBoxToPublic insertUri is Null");
                            }
                            OutputStream outputStream = null;
                            FileInputStream fileInputStream = null;
                            try {
                                outputStream = contentHelper.resolver.openOutputStream(contentHelper.insertUri);
                                fileInputStream = new FileInputStream(file);
                                byte[] bt = new byte[2048];
                                int c;
                                while ((c = fileInputStream.read(bt)) > 0) {
                                    outputStream.write(bt, 0, c);
                                }
                                fileInputStream.close();
                                outputStream.close();
                                contentHelper.values.clear();
                                contentHelper.values.put(MediaStore.MediaColumns.IS_PENDING, 0);
                                contentHelper.update();
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.close();
                                    }
                                    if (fileInputStream != null) {
                                        fileInputStream.close();
                                    }
                                } catch (IOException e) {
                                }
                            }
                        } else {
                            File fileDir = PartStaticFinalUtil.getFileDirByType(mimeType);
                            File saveFile = new File(fileDir, fileName + "." + fileType);
                            if (saveFile.exists()) {
                                saveFile = new File(fileDir, fileName + "_" + System.currentTimeMillis() + "." + fileType);
                            }
                            OutputStream outputStream = null;
                            FileInputStream fileInputStream = null;
                            boolean isSuccess = false;
                            try {
                                outputStream = new FileOutputStream(saveFile);
                                fileInputStream = new FileInputStream(file);
                                byte[] bt = new byte[2048];
                                int c;
                                while ((c = fileInputStream.read(bt)) > 0) {
                                    outputStream.write(bt, 0, c);
                                }
                                isSuccess = true;
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.flush();
                                        outputStream.close();
                                    }
                                    if (isSuccess){
                                        FileUtils.freshFile(appContext, saveFile);
                                    }
                                    if (fileInputStream != null) {
                                        fileInputStream.close();
                                    }
                                } catch (IOException e) {
                                }
                            }
                        }
                        return 1;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer flag) throws Throwable {
                        if (fileSaveListener != null) {
                            fileSaveListener.onComplete();
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Throwable {
                        if (fileSaveListener != null) {
                            fileSaveListener.onError(throwable);
                        }
                        String eMessage = throwable.getMessage();
                        if (TextUtils.isEmpty(eMessage)){
                            eMessage = "保存失败";
                        }
                        Toast.makeText(appContext, eMessage, Toast.LENGTH_SHORT).show();
                    }
                });
    }

    /**
     * @param base64File   base64文件字符串
     * @param fileFullName 文件全名 非 path
     * @param fileType     文件后缀名 无"."
     */
    public static void saveFile(Context appContext, String base64File, String fileFullName, String fileType, IFileSaveListener fileSaveListener) {
        Observable.just(base64File)
                .map(new Function<String, Integer>() {
                    @Override
                    public Integer apply(String base64File) throws Throwable {
                        String fileName = getFileNameNoExt(fileFullName);
                        String mimeType = getMimeTypeByFileType(fileType);
                        byte[] fileByte = Base64.decode(base64File, Base64.DEFAULT);
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                            ContentHelper contentHelper = new ContentHelper(appContext, fileName, mimeType);
                            if (contentHelper.insertUri == null) {
                                Log.e("PartitionStorageUtil", "sandBoxToPublic insertUri is Null");
                                throw new Exception("sandBoxToPublic insertUri is Null");
                            }
                            OutputStream outputStream = null;
                            try {
                                outputStream = contentHelper.resolver.openOutputStream(contentHelper.insertUri);
                                outputStream.write(fileByte);
                                outputStream.close();
                                contentHelper.values.clear();
                                contentHelper.values.put(MediaStore.MediaColumns.IS_PENDING, 0);
                                contentHelper.update();
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.close();
                                    }
                                } catch (IOException e) {
                                }
                            }
                        } else {
                            File fileDir = PartStaticFinalUtil.getFileDirByType(mimeType);
                            File saveFile = new File(fileDir, fileName + "." + fileType);
                            if (saveFile.exists()) {
                                saveFile = new File(fileDir, fileName + "_" + System.currentTimeMillis() + "." + fileType);
                            }
                            OutputStream outputStream = null;
                            boolean isSuccess = false;
                            try {
                                outputStream = new FileOutputStream(saveFile);
                                outputStream.write(fileByte);
                                isSuccess = true;
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.flush();
                                        outputStream.close();
                                    }
                                    if (isSuccess){
                                        FileUtils.freshFile(appContext, saveFile);
                                    }
                                } catch (IOException e) {
                                }
                            }
                        }
                        return 1;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer flag) throws Throwable {
                        Toast.makeText(appContext, "保存成功！", Toast.LENGTH_SHORT).show();
                        if (fileSaveListener != null) {
                            fileSaveListener.onComplete();
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Throwable {
                        if (fileSaveListener != null) {
                            fileSaveListener.onError(throwable);
                        }
                        String eMessage = throwable.getMessage();
                        if (TextUtils.isEmpty(eMessage)){
                            eMessage = "保存失败";
                        }
                        Toast.makeText(appContext, eMessage, Toast.LENGTH_SHORT).show();
                    }
                });
    }

    /**
     * @param fileInputStream 输入流
     * @param fileFullName    文件全名 非 path
     * @param fileType        文件后缀名 无"."
     */
    public static void saveFile(Context appContext, FileInputStream fileInputStream, String fileFullName, String fileType, IFileSaveListener fileSaveListener) {
        Observable.just(fileInputStream)
                .map(new Function<FileInputStream, Integer>() {
                    @Override
                    public Integer apply(FileInputStream fileInputStream) throws Throwable {
                        String fileName = getFileNameNoExt(fileFullName);
                        String mimeType = getMimeTypeByFileType(fileType);
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                            ContentHelper contentHelper = new ContentHelper(appContext, fileName, mimeType);
                            if (contentHelper.insertUri == null) {
                                Log.e("PartitionStorageUtil", "sandBoxToPublic insertUri is Null");
                                throw new Exception("sandBoxToPublic insertUri is Null");
                            }
                            OutputStream outputStream = null;
                            try {
                                outputStream = contentHelper.resolver.openOutputStream(contentHelper.insertUri);
                                byte[] bt = new byte[2048];
                                int c;
                                while ((c = fileInputStream.read(bt)) > 0) {
                                    outputStream.write(bt, 0, c);
                                }
                                fileInputStream.close();
                                outputStream.close();

                                contentHelper.values.clear();
                                contentHelper.values.put(MediaStore.MediaColumns.IS_PENDING, 0);
                                contentHelper.update();
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.close();
                                    }
                                    if (fileInputStream != null) {
                                        fileInputStream.close();
                                    }
                                } catch (IOException e) {
                                }
                            }
                        } else {
                            File fileDir = PartStaticFinalUtil.getFileDirByType(mimeType);
                            File saveFile = new File(fileDir, fileName + "." + fileType);
                            if (saveFile.exists()) {
                                saveFile = new File(fileDir, fileName + "_" + System.currentTimeMillis() + "." + fileType);
                            }
                            OutputStream outputStream = null;
                            boolean isSuccess = false;
                            try {
                                outputStream = new FileOutputStream(saveFile);
                                byte[] bt = new byte[2048];
                                int c;
                                while ((c = fileInputStream.read(bt)) > 0) {
                                    outputStream.write(bt, 0, c);
                                }
                                isSuccess = true;
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.flush();
                                        outputStream.close();
                                    }
                                    if (isSuccess){
                                        FileUtils.freshFile(appContext, saveFile);
                                    }
                                    if (fileInputStream != null) {
                                        fileInputStream.close();
                                    }
                                } catch (IOException e) {
                                }
                            }
                        }
                        return 1;
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer flag) throws Throwable {
                        if (fileSaveListener != null) {
                            fileSaveListener.onComplete();
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Throwable {
                        if (fileSaveListener != null) {
                            fileSaveListener.onError(throwable);
                        }
                        String eMessage = throwable.getMessage();
                        if (TextUtils.isEmpty(eMessage)){
                            eMessage = "保存失败";
                        }
                        Toast.makeText(appContext, eMessage, Toast.LENGTH_SHORT).show();
                    }
                });
    }

    /**
     * 将内部文件转移到外部存储
     *
     * @param file 内部存储文件
     */
    @RequiresApi(api = Build.VERSION_CODES.Q)
    public static void sandPrivateToPublic(Context appContext, @NotNull File file, IFileSaveListener fileSaveListener) {
        Observable.just(file)
                .map(new Function<File, Integer>() {
                    @Override
                    public Integer apply(File file) throws Throwable {
                        String fileName = getFileNameNoExt(file.getName());
                        String fileType = MimeTypeMap.getFileExtensionFromUrl(file.getPath());
                        String mimeType = getMimeTypeByFileType(fileType);
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                            ContentHelper contentHelper = new ContentHelper(appContext, fileName, mimeType);
                            if (contentHelper.insertUri == null) {
                                Log.e("PartitionStorageUtil", "sandBoxToPublic insertUri is Null");
                                throw new Exception("sandBoxToPublic insertUri is Null");
                            }
                            OutputStream outputStream = null;
                            FileInputStream fileInputStream = null;
                            try {
                                outputStream = contentHelper.resolver.openOutputStream(contentHelper.insertUri);
                                fileInputStream = new FileInputStream(file);
                                byte[] bt = new byte[2048];
                                int c;
                                while ((c = fileInputStream.read(bt)) > 0) {
                                    outputStream.write(bt, 0, c);
                                }
                                fileInputStream.close();
                                outputStream.close();

                                contentHelper.values.clear();
                                contentHelper.values.put(MediaStore.MediaColumns.IS_PENDING, 0);
                                contentHelper.update();
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.close();
                                    }
                                    if (fileInputStream != null) {
                                        fileInputStream.close();
                                    }
                                } catch (IOException e) {
                                }
                            }
                        } else {
                            File fileDir = PartStaticFinalUtil.getFileDirByType(mimeType);
                            File saveFile = new File(fileDir, fileName + "." + fileType);
                            if (saveFile.exists()) {
                                saveFile = new File(fileDir, fileName + "_" + System.currentTimeMillis() + "." + fileType);
                            }
                            OutputStream outputStream = null;
                            FileInputStream fileInputStream = null;
                            boolean isSuccess = false;
                            try {
                                outputStream = new FileOutputStream(saveFile);
                                fileInputStream = new FileInputStream(file);
                                byte[] bt = new byte[2048];
                                int c;
                                while ((c = fileInputStream.read(bt)) > 0) {
                                    outputStream.write(bt, 0, c);
                                }
                                isSuccess = true;
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.flush();
                                        outputStream.close();
                                    }
                                    if (isSuccess){
                                        FileUtils.freshFile(appContext, saveFile);
                                    }
                                    if (fileInputStream != null) {
                                        fileInputStream.close();
                                    }
                                } catch (IOException e) {
                                }
                            }
                        }
                        return 1;
                    }
                }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Throwable {
                        Toast.makeText(appContext, "保存成功！", Toast.LENGTH_SHORT).show();
                        if (fileSaveListener != null) {
                            fileSaveListener.onComplete();
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Throwable {
                        if (fileSaveListener != null) {
                            fileSaveListener.onError(throwable);
                        }
                        String eMessage = throwable.getMessage();
                        if (TextUtils.isEmpty(eMessage)){
                            eMessage = "保存失败";
                        }
                        Toast.makeText(appContext, eMessage, Toast.LENGTH_SHORT).show();
                    }
                });
    }


    /**
     * 将外部存储转移到内部文件
     *
     * @param uri 外部存储uri
     * @param file 内部存储文件(重复文件将被移除),文件名记得有后缀名的哦，要么不支持
     */
    @RequiresApi(api = Build.VERSION_CODES.Q)
    public static void sandPublicToPrivate(Context appContext, Uri uri, @NotNull File file, IFileSaveListener fileSaveListener) {
        Observable.just(file)
                .map(new Function<File, Integer>() {
                    @Override
                    public Integer apply(File file) throws Throwable {
                        String fileName = getFileNameNoExt(file.getName());
                        String fileType = MimeTypeMap.getFileExtensionFromUrl(file.getPath());
                        String mimeType = getMimeTypeByFileType(fileType);
                        if (file.exists()){
                            file.delete();
                        }
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                            OutputStream outputStream = null;
                            InputStream inputStream = null;
                            try {
                                inputStream = appContext.getContentResolver().openInputStream(uri);
                                outputStream = new FileOutputStream(file);
                                byte[] bt = new byte[2048];
                                int c;
                                while ((c = inputStream.read(bt)) > 0) {
                                    outputStream.write(bt, 0, c);
                                }
                                outputStream.flush();
                                outputStream.close();
                                inputStream.close();
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.flush();
                                        outputStream.close();
                                    }
                                    if (inputStream != null) {
                                        inputStream.close();
                                    }
                                } catch (IOException e) {
                                }
                            }
                        } else {
                            File fileDir = PartStaticFinalUtil.getFileDirByType(mimeType);
                            File saveFile = new File(fileDir, fileName + "." + fileType);
                            if (saveFile.exists()) {
                                saveFile = new File(fileDir, fileName + "_" + System.currentTimeMillis() + "." + fileType);
                            }
                            OutputStream outputStream = null;
                            FileInputStream fileInputStream = null;
                            boolean isSuccess = false;
                            try {
                                outputStream = new FileOutputStream(saveFile);
                                fileInputStream = new FileInputStream(file);
                                byte[] bt = new byte[2048];
                                int c;
                                while ((c = fileInputStream.read(bt)) > 0) {
                                    outputStream.write(bt, 0, c);
                                }
                                isSuccess = true;
                            } catch (Exception e) {
                                throw new IOException("存储失败");
                            } finally {
                                try {
                                    if (outputStream != null) {
                                        outputStream.flush();
                                        outputStream.close();
                                    }
                                    if (isSuccess){
                                        FileUtils.freshFile(appContext, saveFile);
                                    }
                                    if (fileInputStream != null) {
                                        fileInputStream.close();
                                    }
                                } catch (IOException e) {
                                }
                            }
                        }
                        return 1;
                    }
                }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer integer) throws Throwable {
                        Toast.makeText(appContext, "保存成功！", Toast.LENGTH_SHORT).show();
                        if (fileSaveListener != null) {
                            fileSaveListener.onComplete();
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Throwable {
                        if (fileSaveListener != null) {
                            fileSaveListener.onError(throwable);
                        }
                        String eMessage = throwable.getMessage();
                        if (TextUtils.isEmpty(eMessage)) {
                            eMessage = "保存失败";
                        }
                        Toast.makeText(appContext, eMessage, Toast.LENGTH_SHORT).show();
                    }
                });
    }

    /**
     * 通过文件无"."后缀名，获取mimetype类型
     *
     * @param fileType 文件后缀名
     * @return mime类型
     * @throws UnsupportedSchemeException
     */
    public static String getMimeTypeByFileType(String fileType) throws UnsupportedSchemeException {
        String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileType);
        if (TextUtils.isEmpty(mimeType)) {
            throw new UnsupportedSchemeException("不支持的类型");
        }
        return mimeType;
    }

    /**
     * 通过文件地址获取mimetype类型
     *
     * @param filePath 文件地址
     * @return mime类型
     * @throws UnsupportedSchemeException
     */
    private static String getMimeTypeByFilePath(String filePath) throws UnsupportedSchemeException {
        String ext = MimeTypeMap.getFileExtensionFromUrl(filePath);
        String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext);
        if (TextUtils.isEmpty(mimeType)) {
            throw new UnsupportedSchemeException("不支持的类型");
        }
        return mimeType;
    }

    /**
     * 通过切除最后一个"."去掉文件名后缀名
     * 请勿使用文件地址，引起非必要错误
     */
    public static String getFileNameNoExt(String filename) {
        if (!TextUtils.isEmpty(filename)) {
            int dot = filename.lastIndexOf('.');
            if (dot > -1) {
                return filename.substring(0, dot);
            }
        }
        return filename;
    }
}